package com.spring.springboot.sys.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.spring.springboot.sys.core.config.MinioProperties;
import com.spring.springboot.sys.core.constant.BusinessConstant;
import com.spring.springboot.sys.core.constant.ConstantsContext;
import com.spring.springboot.sys.core.model.UploadResponse;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Random;

/**
 * @author admin
 * @date 2022-06-01 22:47:22:47
 * @description minio文件上传工具类
 */
@Component
public class MinioUtils {

    @Resource
    private MinioClient minioClient;

    private static MinioClient client;

    @PostConstruct
    public void init() {
        client = minioClient;
    }

    /**
     * 上传文件
     *
     * @param file       文件
     * @param bucketName 桶名称
     * @return UploadResponse
     * @throws java.lang.Exception 抛出异常
     */
    public static UploadResponse uploadFile(MultipartFile file, String bucketName) throws Exception {
        //获取缓存的minio配置参数
        MinioProperties minioProperties = ConstantsContext.getMinioProperties();
        //判断文件是否为空
        if (null == file || 0 == file.getSize()) {
            return null;
        }
        //判断存储桶是否存在  不存在则创建
        createBucket(bucketName);
        //桶策略设置为
        client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(json2String("policyJson.json", bucketName)).build());
        //文件名
        String originalFilename = file.getOriginalFilename();
        //新的文件名 = 存储桶文件名_时间戳.后缀名
        assert originalFilename != null;
        SimpleDateFormat format = new SimpleDateFormat(BusinessConstant.DateFormat.DATE_FORMAT);
        String fileName = bucketName + "_" +
                System.currentTimeMillis() + "_" + format.format(new Date()) + "_" + new Random().nextInt(1000) +
                originalFilename.substring(originalFilename.lastIndexOf("."));
        //开始上传
        client.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(fileName)
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build());
        String url = minioProperties.getEndpoint() + "/" + bucketName + "/" + fileName;
        String urlHost = minioProperties.getNginxHost() + "/" + bucketName + "/" + fileName;
        return new UploadResponse(url, urlHost);
    }

    /**
     * 创建bucket
     */
    public static void createBucket(String bucketName) throws Exception {
        if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
            client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 获取全部bucket
     *
     * @return List<Bucket>
     */
    public static List<Bucket> getAllBuckets() throws Exception {
        return client.listBuckets();
    }

    /**
     * 根据bucketName获取信息
     *
     * @param bucketName bucket名称
     */
    public static Optional<Bucket> getBucket(String bucketName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, InternalException, ErrorResponseException, XmlParserException, ServerException {
        return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
    }

    /**
     * 根据bucketName删除信息
     *
     * @param bucketName bucket名称
     */
    public static void removeBucket(String bucketName) throws Exception {
        client.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    /**
     * 获取⽂件外链
     *
     * @param bucketName bucket名称
     * @param objectName ⽂件名称
     * @param expires    过期时间 <=7
     * @return url
     */
    public static String getObjectURL(String bucketName, String objectName, Integer expires) throws Exception {
        return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).expiry(expires).build());
    }

    /**
     * 获取⽂件
     *
     * @param bucketName bucket名称
     * @param objectName ⽂件名称
     * @return ⼆进制流
     */
    public static InputStream getObject(String bucketName, String objectName) throws Exception {
        return client.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 上传⽂件
     *
     * @param bucketName bucket名称
     * @param objectName ⽂件名称
     * @param stream     ⽂件流
     * @throws java.lang.Exception <a href="https://docs.minio.io/cn/java-client-api-reference.html#putObject">...</a>
     */
    public static void putObject(String bucketName, String objectName, InputStream stream) throws Exception {
        client.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, stream.available(), -1).contentType(objectName.substring(objectName.lastIndexOf("."))).build());
    }

    /**
     * 上传⽂件
     *
     * @param bucketName  bucket名称
     * @param objectName  ⽂件名称
     * @param stream      ⽂件流
     * @param size        ⼤⼩
     * @param contextType 类型
     * @throws java.lang.Exception <a href="https://docs.minio.io/cn/java-client-api-reference.html#putObject">...</a>
     */
    public static void putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {
        client.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, size, -1).contentType(contextType).build());
    }

    /**
     * 获取⽂件信息
     *
     * @param bucketName bucket名称
     * @param objectName ⽂件名称
     * @throws java.lang.Exception <a href="https://docs.minio.io/cn/java-client-api-reference.html#statObject">...</a>
     */
    public static StatObjectResponse getObjectInfo(String bucketName, String objectName) throws Exception {
        return client.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 删除⽂件
     *
     * @param bucketName bucket名称
     * @param objectName ⽂件名称
     * @throws java.lang.Exception <a href="https://docs.minio.io/cn/java-client-apireference.html#removeObject">...</a>
     */
    public static void removeObject(String bucketName, String objectName) throws Exception {
        client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * json转String
     *
     * @param path       文件路径
     * @param bucketName 这里是传入的桶名称，执行完后会替换掉原json文件中的MyBucketName
     * @return String
     * @throws java.io.IOException 抛出io异常
     */
    public static String json2String(String path, String bucketName) throws IOException {
        StringBuilder result = new StringBuilder();

        ClassPathResource resource = new ClassPathResource(path);
        InputStream in = resource.getInputStream();
        // 读取文件上的数据。 将字节流向字符流的转换。读取
        InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
        // 创建字符流缓冲区   缓冲
        BufferedReader bufferedReader = new BufferedReader(isr);

        String line;
        while ((line = bufferedReader.readLine()) != null) {
            result.append(System.lineSeparator()).append(line);
        }
        isr.close();
        JSONObject parse = (JSONObject) JSONObject.parse(result.toString());
        JSONArray statement = (JSONArray) parse.get("Statement");
        JSONObject o = (JSONObject) statement.get(0);
        o.remove("Resource");
        o.fluentPut("Resource", "arn:aws:s3:::" + bucketName);
        JSONObject o1 = (JSONObject) statement.get(1);
        o1.remove("Resource");
        o1.fluentPut("Resource", "arn:aws:s3:::" + bucketName);
        JSONObject o2 = (JSONObject) statement.get(2);
        o2.remove("Resource");
        o2.fluentPut("Resource", "arn:aws:s3:::" + bucketName + "/**");
        return parse.toString();
    }

}
